package com.flower.oauth2.config;

import com.flower.oauth2.entity.SysUser;
import com.flower.oauth2.service.Impl.SysUserServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import javax.sql.DataSource;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName AuthorizationServerConfig
 * @Description 授权服务器配置
 * @Date 2021/08/24
 * @Author wuhanxiong
 */

@Configuration
@EnableAuthorizationServer
@AllArgsConstructor
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    private DataSource dataSource;
    private AuthenticationManager authenticationManager;
    private SysUserServiceImpl sysUserServiceImpl;

    @Bean
    public JdbcClientDetailsService jdbcClientDetailsService(){
        return  new JdbcClientDetailsService(dataSource);
    }


    /**
     * 配置 Token 节点的安全策略,也就是配置令牌token的访问限制
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //TODO 开启/oauth/token_key 获取token不需要权限访问
                .tokenKeyAccess("permitAll()")
                //TODO 开启/oauth/check_token 验证token 需要认证权限访问
                .checkTokenAccess("isAuthenticated()")
                //TODO 允许表单认证
                .allowFormAuthenticationForClients();
    }

    /**
     * 配置客户端详情信息
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //TODO OAuth2客户端[数据库加载]
        clients.withClientDetails(jdbcClientDetailsService());
        /*//TODO 客户端存入内存模式,适合测试环境使用
        clients.inMemory()
                //TODO 客户端id
                .withClient("client_id")
                //TODO 客户端认证密码
                .secret("secret")
                //TODO 授权类型有四种,一般是授权码authorization_code和密码password两种类型
                .authorizedGrantTypes("authorization_code","refresh_token")
                //TODO 授权范围
                .scopes("app")
                //TODO 登录后绕过批准询问
                .autoApprove(true);*/
    }

    /**
     * 配置授权token 节点和token 服务
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)  {
        //TODO 配置jwt无状态存储token+jwt自定义内容增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
        //TODO 添加jwt自定义增强
        tokenEnhancers.add(tokenEnhancer());
        //TODO 添加jwt token签名算法
        tokenEnhancers.add(jwtAccessTokenConverter());
        tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);


        endpoints.authenticationManager(authenticationManager)
                .accessTokenConverter(jwtAccessTokenConverter())
                .tokenEnhancer(tokenEnhancerChain)
                .userDetailsService(sysUserServiceImpl)
                // refresh_token有两种使用方式：重复使用(true)、非重复使用(false)，默认为true
                // 1.重复使用：access_token过期刷新时， refresh token过期时间未改变，仍以初次生成的时间为准
                // 2.非重复使用：access_token过期刷新时， refresh_token过期时间延续，在refresh_token有效期内刷新而无需失效再次登录
                .reuseRefreshTokens(false);
    }

    /**
     * 使用非对称加密算法对token签名
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setKeyPair(keyPair());
        return converter;
    }

    /**
     * 从classpath下的密钥库中获取密钥对(公钥+私钥)
     */
    @Bean
    public KeyPair keyPair() {
        KeyStoreKeyFactory factory = new KeyStoreKeyFactory(
                new ClassPathResource("flower.jks"), "flower".toCharArray());
        KeyPair keyPair = factory.getKeyPair(
                "flower", "flower".toCharArray());
        return keyPair;
    }

    /**
     * JWT内容增强
     */
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            Map<String, Object> map = new HashMap<>(2);
            User user = (User) authentication.getUserAuthentication().getPrincipal();
            map.put("username", user.getUsername());
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(map);
            return accessToken;
        };
    }

}
